package com.oig.sys.auth.controller;

import com.oig.common.constants.CommonConstants;
import com.oig.common.exception.ServiceException;
import com.oig.common.module.SysResponse;
import com.oig.common.util.JsonUtil;
import com.oig.oauth.api.OauthService;
import com.oig.sys.auth.constants.AuthEnums;
import com.oig.sys.auth.dto.LoginDto;
import com.oig.sys.auth.dto.OpenUserDto;
import com.oig.sys.auth.service.ITbOpenAuthService;
import com.oig.sys.auth.service.ITbOpenUserService;
import com.oig.sys.auth.service.ITbUserService;
import com.oig.sys.auth.vo.LoginVo;
import com.oig.sys.auth.vo.OpenAuthVo;
import com.oig.sys.auth.vo.UserVo;
import com.oig.sys.security.config.CacheConstants;
import com.oig.sys.security.support.SecurityUser;
import com.oig.sys.security.util.SecurityUtil;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.swagger.v3.oas.annotations.Hidden;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.xml.bind.DatatypeConverter;
import java.util.Map;

/**
 * 流程参看sso.jpg
 * 暂时放这里用
 */
@Tag(name = "辅助前后端分离的oauth2的登录")
@Slf4j
@RestController
public class OAuth2Controller {

    @Value("${sys.oauth2.client-id}")
    private String clientId;
    @Value("${sys.oauth2.client-secret}")
    private String clientSecret;
    @Value("${sys.oauth2.redirect-uri}")
    private String redirectUri;

    private final OauthService oauthService;
    private final ITbUserService userService;
    private final ITbOpenAuthService openAuthService;
    private final ITbOpenUserService openUserService;

    public OAuth2Controller(OauthService oauthService,ITbUserService userService,
                            ITbOpenAuthService openAuthService, ITbOpenUserService openUserService){
        this.oauthService = oauthService;
        this.userService = userService;
        this.openAuthService = openAuthService;
        this.openUserService = openUserService;
    }

    /**
     * 测试代码，现在没有用。
     * 验证码模式先获取验证码，跳到认证中心登录
     * 认证中心登录成功带授权码code跳转回callback url
     * 再根据 授权码 调用获取access token接口
     */
    @Hidden
    @Operation(summary = "验证码模式获取token")
    @RequestMapping(value = "/getAccessToken",method = {RequestMethod.GET})
    public SysResponse<Map<String,Object>> getAccessToken(String code, String redirectUri){
        log.info("code={},redirectUri={}",code,redirectUri);
        MultiValueMap<String,Object> params = new LinkedMultiValueMap<>() ;
        params.add("grant_type","authorization_code") ;
        params.add("code",code) ;
        params.add("client_id",clientId) ;
        params.add("client_secret",clientSecret) ;
        params.add("response_type","code id_token");
        params.add("redirect_uri",redirectUri==null?this.redirectUri:redirectUri) ;
        // 如果配置了服务降级，那么oauthService返回null
        // 如果没有配置熔断，异常被包装成FeignException直接就抛出了，然后FeignExceptionHandler增强处理
        //返回的是，{"access_token":"","refresh_token":"","expires":""}
        Map<String,Object> rsp = this.oauthService.token(params);
        log.info("oauth rsp:{}",rsp);
        if (rsp==null){
            throw new ServiceException("oauth服务不可用") ;
        }
        return SysResponse.ok(rsp);
    }


    private LoginVo buildVo(Map<String,Object> oauthMap) {
        LoginVo loginVo = new LoginVo();
        loginVo.setAccessToken((String)oauthMap.get(CommonConstants.ACCESS_TOKEN));
        try {
            log.debug("user_info:{}",oauthMap.get(CacheConstants.ATTR_USER));
            Map<?, ?> map = (Map<?, ?>) oauthMap.get(CacheConstants.ATTR_USER) ;
            SecurityUser securityUser = SecurityUtil.changeUser(map);
            BeanUtils.copyProperties(securityUser, loginVo);
        } catch (Exception e){
            log.error(e.getMessage(),e);
            //throw new ServiceException("无法转成 securityUser");
        }
        return loginVo;
    }


    @Operation(summary = "密码模式登录")
    @RequestMapping(value = "/userLogin",method = {RequestMethod.POST})
    public SysResponse<LoginVo> userLogin(@RequestBody LoginDto loginDto, HttpServletRequest request){
        String referer = request.getHeader("referer");
        log.info(referer);
        MultiValueMap<String,Object> params = new LinkedMultiValueMap<>() ;
        params.add("grant_type","password") ;
        params.add("client_id",clientId) ;
        params.add("client_secret",clientSecret) ;

        params.add("username",loginDto.getUsername()) ;
        params.add("password",loginDto.getPassword()) ;
        log.debug("params:{}", params);
        Map<String,Object> rep = this.oauthService.token(params);
        if (rep==null){
            throw new ServiceException("服务不可用") ;
        }
        log.info("token info:{}", rep);
        return SysResponse.ok(buildVo(rep));
    }

    @Operation(summary = "退出登录")
    @PostMapping(value = "/userLogout")
    public SysResponse<String> userLogout(@RequestParam("accessToken") String accessToken){
        log.info("退出登录:{}",accessToken);
        this.oauthService.logout(accessToken);
        return SysResponse.ok(accessToken,"用户退出成功");
    }


//    @Operation(summary = "第三方登录回调地址，根据code取第三方获取到用户信息，然后置换为本系统token，返回前端")
//    @RequestMapping(value = "/thirdAuthed/{thirdId}/{mType}",method = {RequestMethod.GET, RequestMethod.POST})
//    public void thirdAuthed(@PathVariable("thirdId")Integer id,
//                            @PathVariable("mType") String mType,
//                            @RequestParam("code") String code,
//                            @RequestParam("state")String state,
//                            HttpServletResponse response) throws IOException {
//        log.debug("id={},code={} state={}", id, code, state);
//        OpenAuthVo openAuthVo = openAuthService.getOpenAuth(id);
//        String userCode = null;
//        if (AuthEnums.AuthType.FS.getCode().equals(openAuthVo.getAuthType())){
//
//        } else {
//            MultiValueMap<String,Object> paramMap = new LinkedMultiValueMap<>() ;
//            paramMap.add("grant_type","authorization_code");
//            paramMap.add("code",code);
//            paramMap.add("client_id",openAuthVo.getClientId());
//            paramMap.add("client_secret",openAuthVo.getClientSecurity());
//            ResponseEntity<String> tokenRs = restTemplate.postForEntity(openAuthVo.getAuthUrl(),  paramMap, String.class) ;
//            if (tokenRs.getStatusCode() == HttpStatus.OK) {
//                //拿到token
//
//            }
//        }
//        Map<String,Object> params = new HashMap<>() ;
//        params.put("grant_type", CommonConstants.GANT_TYPE_U_CODE) ;
//        params.put("client_id", clientId) ;
//        params.put("client_secret", clientSecret) ;
//        params.put(CommonConstants.GANT_TYPE_U_CODE, userCode) ;
//        Map<String,Object> rsp = this.oauthService.token(params);
//        if (rsp==null){
//            throw new ServiceException("服务不可用") ;
//        }
//        String accessToken = (String) rsp.get(CommonConstants.ACCESS_TOKEN);
//
//        //跳回前端，要拼上token
//        if (AuthEnums.TerminalType.PC.getCode().equals(mType)){
//            response.sendRedirect(openAuthVo.getPcBackUrl()+"?access_token="+accessToken);
//        } else {
//            String backUrl = openAuthVo.getMbBackUrl()==null?openAuthVo.getPcBackUrl() : openAuthVo.getMbBackUrl();
//            response.sendRedirect(backUrl + "?access_token="+accessToken);
//        }
//    }

    @Operation(summary = "用单点登录的token换系统本身的accessToken")
    @RequestMapping(value = "/changeToken", method = {RequestMethod.GET, RequestMethod.POST})
    public SysResponse<LoginVo> changeToken(@RequestParam("token") String token){
        OpenAuthVo openAuthVo = this.openAuthService.getOpenAuthByType(AuthEnums.AuthType.RA.getCode());
        String userCode;
        Claims claims;
        try {
            claims = Jwts.parser()
                    .setSigningKey(DatatypeConverter.parseBase64Binary(openAuthVo.getClientSecurity()))
                    .parseClaimsJws(token).getBody();
            log.debug("token info：{}", claims);
            userCode = (String)claims.get("userCode");
        } catch (Exception e){
            log.warn("token校验不通过", e);
            return SysResponse.failed("token校验不通过");
        }
        if (StringUtils.isBlank(userCode)){
            return SysResponse.failed("没有工号");
        }
        LoginVo loginVo=this.getTokenByUserCode(userCode);
        log.debug("login:{}",loginVo);
        OpenUserDto openUserDto = new OpenUserDto();
        openUserDto.setOpenAuthId(openAuthVo.getId());
        openUserDto.setUserId(loginVo.getId());
        openUserDto.setOpenUserName(String.valueOf(claims.get("name")));
        openUserDto.setOpenUserAcc(String.valueOf(claims.get("userId")));
        openUserDto.setOpenUserInfo(JsonUtil.toJSONString(claims));
        this.openUserService.addOrUpdate(openUserDto);
        return SysResponse.ok(loginVo);
    }

    private LoginVo getTokenByUserCode(String userCode){
        MultiValueMap<String,Object> params = new LinkedMultiValueMap<>() ;
        params.add("grant_type", CommonConstants.GANT_TYPE_U_CODE) ;
        params.add("client_id", clientId) ;
        params.add("client_secret", clientSecret) ;
        params.add(CommonConstants.GANT_TYPE_U_CODE, userCode) ;
        log.debug("获取token:{}", params);
        Map<String,Object> rsp = this.oauthService.token(params);
        if (rsp==null){
            throw new ServiceException("服务不可用") ;
        }
        return buildVo(rsp);
    }


    @Operation(summary = "获取当前用户信息")
    @RequestMapping(value = "/getCurrentUserInfo", method = {RequestMethod.GET})
    public SysResponse<LoginVo> getCurrentUserInfo(){
        SecurityUser user = SecurityUtil.getDefineUserMain() ;
        UserVo userVo = this.userService.getUserById(user.getId());
        LoginVo loginVo = new LoginVo();
        BeanUtils.copyProperties(userVo, loginVo);
        return SysResponse.ok(loginVo);
    }

}
